﻿using System.Linq;
using System.Web.Mvc;
using ExampleWebApplication.Entities;
using ExampleWebApplication.Infraestruture.Aggregates;
using ExampleWebApplication.Infraestruture.EntityFramework;
using ExampleWebApplication.Models;
using Telerik.Web.Mvc;
using TelerikMvcGridCustomBindingHelper;
using System.Data.Entity;

namespace ExampleWebApplication.Controllers
{
    public class EntityFrameworkController : Controller
    {
        readonly NorthwindContext _northwindContext = new NorthwindContext();

        public ActionResult Index()
        {
            return View();
        }

        #region Example #01: Custom ajax binding

        // Here we create our view, no need to pass any data yet.
        public ActionResult CustomAjaxBinding()
        {
            return View();
        }

        // And then send the data through ajax requests.
        [GridAction(EnableCustomBinding = true)]
        public ActionResult CustomAjaxBindingData(GridCommand command)
        {
            // Telerik custom client binding demo: http://demos.telerik.com/aspnet-mvc/razor/grid/ajaxbinding

            // The same query will be used to count and list.
            // WARN: careful with Select N+1: http://nhprof.com/Learn/Alerts/SelectNPlusOne
            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #02: Custom server binding

        // We must enter the name of the grid, otherwise it will not work.
        [GridAction(GridName = "Grid")]
        public ActionResult CustomServerBinding(GridCommand command)
        {
            // Telerik custom server binding demo: http://demos.telerik.com/aspnet-mvc/razor/grid/customserverbinding

            // We have to inform the pageSize here, since in the first call, 
            // the GridCommand is not correctly initialized by the grid, but 
            // dont forget to set it in the grid too.
            command.PageSize = 15; // Please put the same pageSize as is in the view

            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            ViewBag.Total = gridModel.Total;
            ViewBag.Data = gridModel.Data;

            return View();
        }

        #endregion

        #region Example #03: Two queries to count and list data

        public ActionResult TwoQueriesToCountAndList()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult TwoQueriesToCountAndListData(GridCommand command)
        {
            // The same query will be used to count and list.
            // WARN: careful with Select N+1: http://nhprof.com/Learn/Alerts/SelectNPlusOne
            var query = _northwindContext.Products.Include(x => x.Category);
            var queryToCount = _northwindContext.Products;

            var gridQueryProvider = new GridQueryProvider(query, queryToCount);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, gridQueryProvider);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #04: Mapping techniques

        public ActionResult MappingTechniques()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult MappingTechniquesData(GridCommand command)
        {
            var query = _northwindContext.Orders
                .Include(order => order.Customer)
                .Include(order => order.Employee)
                .Include(order => order.Shipper);

            var gridHelper = new GridCustomBindingHelper<Order, OrderModel>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #05: Aggregates server side [NOT SUPPORTED YET]

        // We must enter the name of the grid, otherwise it will not work.
        [GridAction(GridName = "Grid")]
        public ActionResult AggregatesServerSide(GridCommand command)
        {
            // This functionality is NOT YET natively supported by the Telerik Grid:
            // Question 01: http://www.telerik.com/community/forums/aspnet-mvc/grid/aggregates-with-custom-binding.aspx
            // Question 02: http://www.telerik.com/community/forums/aspnet-mvc/grid/custom-binding-and-aggregates-groupfootertemplates.aspx

            // Telerik server aggregates demo: http://demos.telerik.com/aspnet-mvc/razor/grid/aggregatesserverside

            // Believe me, I have tried hard to add this feature, but unfortunately I could not get to it.
            // I assure you that I will continue on my journey to make it work, but the best chance is to wait for native support.
            // If you really need aggregates, try the ajax version as shown in the Example 07: Aggregates client side (Ajax).

            // We have to inform the pageSize here, since in the first call, 
            // the GridCommand is not correctly initialized by the grid, but 
            // dont forget to set it in the grid too.
            command.PageSize = 15; // Please put the same pageSize as is in the grid

            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            ViewBag.Total = gridModel.Total;
            ViewBag.Data = gridModel.Data;

            return View();
        }

        #endregion

        #region Example #06: Aggregates client side (Ajax)

        public ActionResult AggregatesClientSide()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult AggregatesClientSideData(GridCommand command)
        {
            // Telerik demo: http://demos.telerik.com/aspnet-mvc/razor/grid/aggregatesajax

            // This functionality (aggregates with custom binding) is NOT YET natively supported by the Telerik Grid:
            // Question 01: http://www.telerik.com/community/forums/aspnet-mvc/grid/aggregates-with-custom-binding.aspx
            // Question 02: http://www.telerik.com/community/forums/aspnet-mvc/grid/custom-binding-and-aggregates-groupfootertemplates.aspx

            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #07: Direct usage of a TEntity (without TEntity to TViewModel mappings)

        public ActionResult DirectUsageOfTEntity()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult DirectUsageOfTEntityData(GridCommand command)
        {
            var query = _northwindContext.Suppliers;

            var gridHelper = new GridCustomBindingHelper<Supplier>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #08: Custom aggregates functions - client side (Ajax)

        public ActionResult CustomAggregatesFunctionsClientSide()
        {
            return View();
        }

        public ActionResult CustomAggregatesFunctionsInlineClientSide()
        {
            return View();
        }

        public ActionResult CustomAggregatesFunctionsIgnoreGroupAggregatesClientSide()
        {
            return View();
        }

        public ActionResult CustomAggregatesFunctionsInlineDirectUsageOfTEntityClientSide()
        {
            return View();
        }

        // Example #08.1: CustomAggregates via an inherited class
        [GridAction(EnableCustomBinding = true)]
        public ActionResult CustomAggregatesFunctionsClientSideData(GridCommand command)
        {
            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query)
                .AddAggregateFunction<SumUnitPriceMultipliedByUnitsOnOrderAggregateFunction>(product => product.Units_OnOrder);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        // Example #08.2: CustomAggregates via an inline function
        [GridAction(EnableCustomBinding = true)]
        public ActionResult CustomAggregatesFunctionsInlineClientSideData(GridCommand command)
        {
            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query)
                .AddAggregateFunction(product => product.Units_OnOrder, products => products.Sum(x => x.UnitPrice * x.Units_On_Order));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        // Example #08.3: CustomAggregates ignoring group aggregates
        [GridAction(EnableCustomBinding = true)]
        public ActionResult CustomAggregatesFunctionsIgnoreGroupAggregatesClientSideData(GridCommand command)
        {
            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query)
                .AddAggregateFunction<SumUnitPriceMultipliedByUnitsOnOrderAggregateFunction>(product => product.Units_OnOrder)
                .IgnoreGroupAggregates();

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        // Example #08.4: CustomAggregates via an inline function and direct usage of a TEntity (without TEntity to TViewModel mappings)
        [GridAction(EnableCustomBinding = true)]
        public ActionResult CustomAggregatesFunctionsInlineDirectUsageOfTEntityClientSideData(GridCommand command)
        {
            var query = _northwindContext.Suppliers;

            var gridHelper = new GridCustomBindingHelper<Supplier>(command, query)
                .AddAggregateFunction(supplier => supplier.SupplierID, suppliers => suppliers.Sum(x => x.SupplierID));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #09: Using projections to return only the necessary columns from the database [NOT SUPPORTED YET]

        public ActionResult UsingProjections()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult UsingProjectionsData(GridCommand command)
        {
            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query)
                .UseProjections();
                //.UseProjections(options => options.Ignore(product => product.Discontinued));
                //.UseProjections(options => options.Include(product => product.Discontinued));
                //.UseProjections(options => options.Use(product => product.Discontinued));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #10: BooleanSubstitutes (See the mappings on Global.asax)

        public ActionResult BooleanSubstitute()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult BooleanSubstituteData(GridCommand command)
        {
            // Add a BooleanSubstitute for a specifique property
            //GridModelMapper.CreateMap<Customer, CustomerModel>()
            //    .MapProperty(customer => customer.Bool, model => model.Bool)
            //    .WithBooleanSubstitute<EfBooleanSubstitute>();

            // Add a GlobalBooleanSubstitute for any boolean property
            //GridModelMapper.Configuration.SetGlobalBooleanSubstitute<EfGlobalBooleanSubstitute>();

            var query = _northwindContext.Customers;

            var gridHelper = new GridCustomBindingHelper<Customer, CustomerModel>(command, query);

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #11: ValueResolvers (See the mappings on Global.asax)

        public ActionResult ValueResolver()
        {
            return View();
        }
        
        [GridAction(EnableCustomBinding = true)]
        public ActionResult ValueResolverData(GridCommand command)
        {
            // Add a DecimalValueResolver for a specifique property
            //GridModelMapper.CreateMap<Order, OrderModel>()
            //    .MapProperty(order => order.Freight, model => model.Freight)
            //    .WithResolver<DecimalValueResolver>();

            // Add a GlobalDecimalValueResolver for any property
            //GridModelMapper.Configuration.AddGlobalValueResolver<GlobalDecimalValueResolver>();

            var query = _northwindContext.Orders
                .Include(order => order.Customer)
                .Include(order => order.Employee)
                .Include(order => order.Shipper);

            var gridHelper = new GridCustomBindingHelper<Order, OrderModel>(command, query).UseProjections(options => options.Ignore(order => order.Customer.Bool));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #12: Filtering on nullable values

        public ActionResult FilteringNullableValues()
        {
            return View();
        }

        // Example #12.1:AcceptNullValuesWhenFiltering
        [GridAction(EnableCustomBinding = true)]
        public ActionResult FilteringNullableValuesData(GridCommand command)
        {
            var query = _northwindContext.Orders;

            // Can be set globally for all entities
            //GridModelMapper.Configuration.AcceptNullValuesWhenFiltering();

            // Can be set globally for a specifique entity
            //GridModelMapper.Configuration.AcceptNullValuesWhenFiltering<Product>();

            // Can be set for a specifique property
            //GridModelMapper.CreateMap<Order, OrderModel>()
            //    //.MapProperty(order => order.ShipRegion, model => model.ShipRegion).WithOptions(options => options.AcceptNullValuesWhenFiltering())
            //    .MapProperty(order => order.ShipRegion, model => model.ShipRegion, options => options.AcceptNullValuesWhenFiltering());

            var gridHelper = new GridCustomBindingHelper<Order, OrderModel>(command, query)
                // Can be set for a specifique query
                .AcceptNullValuesWhenFiltering()
                // Let's use projections here to avoid Select+1.
                .UseProjections(options => options.Use(order => new { order.OrderID, order.ShipRegion }));
                //.UseProjections(options => options.Ignore(order => order.Customer.EfBool));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }
        
        public ActionResult FilteringWithNullAliases()
        {
            return View();
        }

        // Example #12.2: FilteringWithNullAliases
        [GridAction(EnableCustomBinding = true)]
        public ActionResult FilteringWithNullAliasesData(GridCommand command)
        {
            // Try searching the column ShipRegion by "none" or "empty", is expected to return null values​​.
            var query = _northwindContext.Orders;

            // Can be set globally for all properties
            //GridModelMapper.Configuration.SetGlobalNullAlias<NullAlias>();

            // Can be set for a specifique property
            //GridModelMapper.CreateMap<Order, OrderModel>()
            //.MapProperty(order => order.ShipRegion, model => model.ShipRegion).WithOptions(options => options.WithNullAlias<ShipRegionNullAlias>())
            //.MapProperty(order => order.ShipRegion, model => model.ShipRegion, options => options.WithNullAlias<ShipRegionNullAlias>());

            var gridHelper = new GridCustomBindingHelper<Order, OrderModel>(command, query)
                // Let's use projections here to avoid Select+1.
                .UseProjections(options => options.Use(order => new { order.OrderID, order.ShipRegion }));
            //.UseProjections(options => options.Ignore(order => order.Customer.EfBool));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        public ActionResult FilteringWithNullSubstitutes()
        {
            return View();
        }

        // Example #12.3 FilteringWithNullSubstitutes
        [GridAction(EnableCustomBinding = true)]
        public ActionResult FilteringWithNullSubstitutesData(GridCommand command)
        {
            var query = _northwindContext.Employees;

            // Can be set globally for all properties
            //GridModelMapper.Configuration.SetGlobalNullSubstitute<EmployeeRegionNullSubstitute>();

            // Can be set for a specifique property
            //GridModelMapper.CreateMap<Employee, EmployeeModel>()
            //    .MapProperty(employee => employee.Region, model => model.Region, options => options.WithNullSubstitute<EmployeeRegionNullSubstitute>());

            var gridHelper = new GridCustomBindingHelper<Employee, EmployeeModel>(command, query)
                .AcceptNullValuesWhenFiltering() // { string.Empty, null } => Aliases that will be treated as null values and will be sent to the database within queries.
                .UseProjections(options => options.Use(employee => new { employee.EmployeeID, employee.FirstName, employee.LastName, employee.Region }));

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion

        #region Example #13: Case insensitive search

        public ActionResult CaseInsensitiveSearch()
        {
            return View();
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult CaseInsensitiveSearchData(GridCommand command)
        {
            var query = _northwindContext.Products.Include(x => x.Category);

            var gridHelper = new GridCustomBindingHelper<Product, ProductModel>(command, query)
                .UseCaseInsensitiveSearch();

            var gridModel = gridHelper.BuildGridModel();

            return View(gridModel);
        }

        #endregion
    }
}
